import * as Crypto from 'crypto-js';
// 按需引入 CryptoJS 需要的组件
// const CryptoJS = require('crypto-js/core')
// const Latin1 = require('crypto-js/enc-latin1')
// const AES = require('crypto-js/aes')
// const ZeroPadding = require('crypto-js/pad-zeropadding')
// const Utf8 = require('crypto-js/enc-utf8')
// const Base64 = require('crypto-js/enc-base64')
import CryptoJS from 'crypto-js/core'
import Latin1 from 'crypto-js/enc-latin1'
import AES from 'crypto-js/aes'
import ZeroPadding from 'crypto-js/pad-zeropadding'
import Utf8 from 'crypto-js/enc-utf8'
import Base64 from 'crypto-js/enc-base64'

const encodeReserveRE = /[!'()*]/g
const encodeReserveReplacer = c => '%' + c.charCodeAt(0).toString(16)
const commaRE = /%2C/g
const encode = str => encodeURIComponent(str)
    .replace(encodeReserveRE, encodeReserveReplacer)
    .replace(commaRE, ',')
const decode = decodeURIComponent

const getKeyHex = cryptoCode => Latin1.parse(cryptoCode);

const getIvHex = (baseCryptoCode) => Latin1.parse(baseCryptoCode);
// 处理key  不足16的补齐0
const fillKey = (key) => {
    let filledKey = null,
        len = key.length;
    if (len <= 16) {
        filledKey = Crypto.enc.Utf8.parse(key.padEnd(16, '0'));
    } else if (len <= 24) {
        filledKey = Crypto.enc.Utf8.parse(key.padEnd(24, '0'));
    } else if (len <= 32) {
        filledKey = Crypto.enc.Utf8.parse(key.padEnd(32, '0'));
    }
    return filledKey;
}


/**
 * 判断字符串是否是base64
 * @param { string } str 
 * @returns { boolean }
 */
function isBase64(str) {
    if (str === '' || str.trim() === '') { return false; }
    try {
        return btoa(atob(str)) == str;
    } catch (err) {
        return false;
    }
}
export default class EasyCropto {
    constructor(options = {}) {
        let defaultOptions = {
            key: 'XXJJDDii',// 对于敏感数据加解密的key
            baseCryptoCode: "这一段文字用来做给路由加密的私钥" // 这个私钥每个项目指定一个唯一。更换密钥，请确认16位
        };
        this.options = { ...defaultOptions, ...options };
    }

    /* 处理前后台敏感数据api */
    // 解密数据
    decryptData(str) {
        const decrypted = Crypto.AES.decrypt(str, fillKey(this.options.key), {
            mode: Crypto.mode.ECB,
            padding: Crypto.pad.Pkcs7
        });
        const plaintext = Crypto.enc.Utf8.stringify(decrypted);
        return plaintext;
    }
    // 加密数据
    encryptData(str) {
        const encrypted = Crypto.AES.encrypt(str, fillKey(this.options.key), {
            mode: Crypto.mode.ECB,
            padding: Crypto.pad.Pkcs7
        })
        let result = encrypted.ciphertext.toString(Crypto.enc.Base64);
        return result;
    }
    /* 
      @desc 批量加解密  params needList:Array  data:Array|Object  type:String(encrypt|decrypt)
      @params 
        needList:Array  需要加解密的字段 要跟后台接口返回的key对应
        data:Array|Object  后台接口返回的数据
        type:String(encrypt|decrypt) 操作类型  默认解密D
      @return Array|Object  解密后数据
    */
    batchDecryptOrEncryptData(needList, data, type = 'D') {
        if (!needList || !Array.isArray(needList)) return

        let dataType = typeof data;
        let fn = type == 'D' ? this.decryptData : this.encryptData
        // 针对数组
        if (dataType == 'object' && Array.isArray(data)) {
            // 小的驱动大的
            needList.forEach(item => {
                data.forEach(dataItem => {
                    if (dataItem[item]) {
                        dataItem[item] = fn(dataItem[item])
                    }
                })
            })
        } else if (dataType == 'object' && !Array.isArray(data) && data != null) {
            needList.forEach(item => {
                if (data[item]) {
                    data[item] = fn(data[item])
                }
            })
        }
        return data
    }
    /* url加解密相关 */
    /**
 * 加密 AES.encrypt
 * @param {String} key
 * @param {String} cryptoCode
 * @returns {string}
 */
    getEncrypt(key, cryptoCode) {
        let keyHex = getKeyHex(cryptoCode || this.options.baseCryptoCode);
        let ivHex = getIvHex(this.options.baseCryptoCode);
        try {
            key = JSON.stringify(key);
        } catch (e) {
            console.warn(e);
        }
        return AES.encrypt(key, keyHex, {
            mode: CryptoJS.mode.CBC,
            padding: ZeroPadding,
            iv: ivHex
        }).toString();
    }

    /**
     * 加密后转base64
     * @param {String}} key 
     * @param {String} cryptoCode 
     */
    getEncryptToBase64(key, cryptoCode) {
        let encryptStr = this.getEncrypt(key, cryptoCode);
        let wordArray = Utf8.parse(encryptStr);
        return Base64.stringify(wordArray);
    }

    /**
     * 解密 AES.decrypt
     * @param data
     * @returns {string}
     */
    getDecrypt(data) {
        let keyHex = getKeyHex(this.options.baseCryptoCode);
        let ivHex = getIvHex(this.options.baseCryptoCode);
        let decrypted = AES.decrypt({
            ciphertext: Base64.parse(data)
        }, keyHex, {
            mode: CryptoJS.mode.CBC,
            padding: ZeroPadding,
            iv: ivHex
        }).toString(Utf8);
        try {
            decrypted = JSON.parse(decrypted);
        } catch (e) {
            console.warn(e);
        }
        return decrypted
    }

    /**
    * 对base64数据解密  先解析base64，在做解密
    * @param {String} data 
    * @returns {string}
    */
    getDecryptByBase64(data) {
        let parsedWordArray = Base64.parse(data);
        let decryptStr = parsedWordArray.toString(Utf8);
        return this.getDecrypt(decryptStr);
    }

    /**
 * 序列化对象 并加密
 * @param {Object} obj 
 */
    stringifyQuery(obj) {
        const res = obj ? Object.keys(obj).map(key => {
            const val = obj[key]
            if (val === undefined) {
                return ''
            }

            if (val === null) {
                return encode(key)
            }

            if (Array.isArray(val)) {
                const result = []
                val.forEach(val2 => {
                    if (val2 === undefined) {
                        return
                    }
                    if (val2 === null) {
                        result.push(encode(key))
                    } else {
                        result.push(encode(key) + '=' + encode(val2))
                    }
                })
                return result.join('&')
            }

            return encode(key) + '=' + encode(val)
        }).filter(x => x.length > 0).join('&') : null

        return res ? `?${this.getEncryptToBase64(res)}` : ''
    }


    /**
     * 解密  反序列化字符串参数
     * @param {String}} query 
     */
    parseQuery(query) {
        const res = {}
        query = query.trim().replace(/^(\?|#|&)/, '')
        if (!query) {
            return res
        }
        // 解密
        query = isBase64(query) ? this.getDecryptByBase64(query) : query;

        query.split('&').forEach(param => {
            const parts = param.replace(/\+/g, ' ').split('=')
            const key = decode(parts.shift())
            const val = parts.length > 0
                ? decode(parts.join('='))
                : null

            if (res[key] === undefined) {
                res[key] = val
            } else if (Array.isArray(res[key])) {
                res[key].push(val)
            } else {
                res[key] = [res[key], val]
            }
        })

        return res
    }
}